package com.ruoyi.equip.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.github.yitter.idgen.YitIdHelper;
import com.ruoyi.common.core.domain.SelectEntity;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.net.HexUtil;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.equip.domain.DeviceInfo;
import com.ruoyi.equip.domain.TagInfo;
import com.ruoyi.equip.domain.cache.DeviceTagCache;
import com.ruoyi.equip.domain.cache.TagCache;
import com.ruoyi.equip.domain.dto.ReaderModelDto;
import com.ruoyi.equip.domain.entity.DeviceTagEntity;
import com.ruoyi.equip.domain.entity.table.DeviceTableEntity;
import com.ruoyi.equip.domain.entity.table.ExportParam;
import com.ruoyi.equip.domain.vo.DeviceDataVo;
import com.ruoyi.equip.domain.vo.DeviceInfoVO;
import com.ruoyi.equip.domain.vo.TagVo;
import com.ruoyi.equip.mapper.DeviceInfoMapper;
import com.ruoyi.equip.mapper.EquipProductMapper;
import com.ruoyi.equip.service.IDeviceInfoService;
import com.ruoyi.equip.service.ITagInfoService;
import com.ruoyi.equip.service.IThingModelService;
import com.ruoyi.equip.util.CacheUtils;
import com.ruoyi.equip.util.HeartCacheUtils;
import com.ruoyi.equip.util.ProductCacheUtils;
import com.ruoyi.equip.vert.config.TcpServerVerticle;
import com.ruoyi.equip.vert.domain.TcpAnsEntity;
import com.ruoyi.equip.vert.domain.TcpParserEntity;
import com.ruoyi.equip.vert.parser.DataReader;
import com.ruoyi.equip.vert.service.impl.ConfigModeTransfer;
import com.ruoyi.td.entity.DeviceProperty;
import com.ruoyi.td.entity.DevicePropertyCache;
import com.ruoyi.td.entity.DevicePropertyLogQueryBo;
import com.ruoyi.td.service.IDeviceInfoData;
import com.ruoyi.td.service.IDevicePropertyData;
import com.ruoyi.td.vo.ThingModelVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.equip.util.CacheUtils.*;
import static com.ruoyi.equip.vert.TcpReaderConstant.*;

/**
 * 网关业务处理逻辑层
 */
@Service
@Slf4j
public class DeviceInfoServiceImpl implements IDeviceInfoService
{


    @Resource
    IThingModelService thingModelService;

    @Resource
    private DeviceInfoMapper deviceInfoMapper;

    @Resource
    IDeviceInfoData deviceInfoData;

    @Resource
    IDevicePropertyData devicePropertyData;

    @Resource
    ITagInfoService tagInfoService;

    @Resource
    EquipProductMapper productMapper;

    @Resource
    ConfigModeTransfer configModeTransfer;

    @PostConstruct
    public void init()
    {
        loadingDeviceCache();
    }

    /**
     * 根据条件分页查询读写器列表
     *
     * @param deviceInfoVO 读写器信息
     * @return 读写器信息集合信息
     */
    @Override
    public List<DeviceInfoVO> selectDeviceInfoList(DeviceInfoVO deviceInfoVO)
    {
        //   除去系统管理员的额外处理
        if (SecurityUtils.hasRole("usergroup")) {
            deviceInfoVO.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
        } else if (SecurityUtils.hasRole("emp")) {
            deviceInfoVO.setUserId(String.valueOf(SecurityUtils.getUserId()));
        }
        List<DeviceInfoVO> deviceInfoVOS = deviceInfoMapper.selectDeviceInfos(deviceInfoVO);
        deviceInfoVOS.forEach(deviceInfoVO1 ->
        {
            deviceInfoVO1.setOnline(HeartCacheUtils.isOnline(deviceInfoVO1.getSbId()));
        });
        return deviceInfoVOS;
    }

    /**
     * 新增读写器信息
     *
     * @param deviceInfo 读写器信息
     * @return 结果
     */
    @Override
    @Transactional
    public int add(DeviceInfo deviceInfo)
    {
        deviceInfo.setId(YitIdHelper.nextId() + "");
        //   除去系统管理员的额外处理
        if (SecurityUtils.hasRole("emp")) {
            deviceInfo.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
            deviceInfo.setUserId(String.valueOf(SecurityUtils.getUserId()));
        }
        int rows = deviceInfoMapper.insert(deviceInfo);

        // 追加缓存
        setDeviceTagCache(deviceInfo);

        // 配置读写器工作模式


        return rows;
    }

    /**
     * 更新读写器设备
     *
     * @param deviceInfo 读写器信息
     * @return 结果
     */
    @Override
    @Transactional
    public int update(DeviceInfo deviceInfo)
    {
        // 获取原始读写器设备ID
        String oldSbId = deviceInfoMapper.selectSbId(String.valueOf(deviceInfo.getId()));

        int i = deviceInfoMapper.updateById(deviceInfo);
        // 更新缓存
        updateDeviceInfo(oldSbId, deviceInfo.getSbId(), deviceInfo);

        // 配置读写器工作模式
        String handerId = DataReader.readHandleMap.get(deviceInfo.getSbId());
        List<String> param = new ArrayList<>();
        String mode = HexUtil.intToHexStr1(deviceInfo.getMode());
        String cycle = HexUtil.intToHexStr2(deviceInfo.getCycle());
        param.add(mode);
        param.add(cycle);
        TcpParserEntity tcpParserEntity = new TcpParserEntity(handerId, deviceInfo.getSbId(), ANS_CONFIG_MODE, param);
        configModeTransfer.transfer(tcpParserEntity);
        return i;
    }

    /**
     * 删除
     *
     * @param deviceId 读写器物理ID
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteById(String deviceId)
    {
        // 查询是否存在子设备
        boolean hasChild = tagInfoService.hasChildOfParentId(deviceId);
        if (hasChild) {
            throw new ServiceException("存在关联标签,不能进行删除");
        }

        String sbId = deviceInfoMapper.selectSbId(deviceId);
        int i = deviceInfoMapper.deleteById(deviceId);
        // 清理缓存
        deleteDeviceTagCache(sbId);
        return i;
    }


    /**
     * 获取指定读写器详情
     *
     * @param deviceId 读写器物理ID
     */
    @Override
    public DeviceInfoVO getDevicesDetail(String deviceId)
    {
        DeviceInfoVO deviceInfoVO = deviceInfoMapper.selectDetail(deviceId);
        deviceInfoVO.setProperty(deviceInfoData.getProperties(deviceId));
        return deviceInfoVO;
    }

    /**
     * 获取读写器属性历史数据
     *
     * @param logQueryBo 物模型单属性历史查询搜索实体类
     */
    @Override
    public List<DeviceProperty> getPropertyHistory(DevicePropertyLogQueryBo logQueryBo)
    {
        DeviceInfo deviceInfo = deviceInfoMapper.selectById(logQueryBo.getDeviceId());
        if (deviceInfo == null) {
            return null;
        }
        logQueryBo.setProductKey(deviceInfo.getProductKey());
        return devicePropertyData.findDevicePropertyHistory(logQueryBo);
    }

    /**
     * 查询所有读写器下拉框
     *
     * @return 读写器下拉框
     */
    @Override
    public List<SelectEntity> selectList()
    {
        DeviceInfo deviceInfo = new DeviceInfo();
        if (SecurityUtils.hasRole("usergroup")) {
            deviceInfo.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
        } else if (SecurityUtils.hasRole("emp")) {
            deviceInfo.setUserId(String.valueOf(SecurityUtils.getUserId()));
        }
        return deviceInfoMapper.selectOptions(deviceInfo);
    }

    /**
     * 获取指定读写器物模型
     *
     * @param deviceId 读写器物理ID
     * @return 指定读写器物模型属性
     */
    @Override
    public List<ThingModelVo.Property> getThingModelOfId(String deviceId)
    {
        String productKey = deviceInfoMapper.selectProductKey(deviceId);
        return thingModelService.findByProductKey(productKey).getModel().getProperties();
    }

    /**
     * 查询所有读写器经纬度
     *
     * @return 查询所有读写器经纬度
     */
    @Override
    public List<DeviceInfoVO> getGis()
    {
        DeviceInfo deviceInfo = new DeviceInfo();
        if (SecurityUtils.hasRole("usergroup")) {
            deviceInfo.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
        } else if (SecurityUtils.hasRole("emp")) {
            deviceInfo.setUserId(String.valueOf(SecurityUtils.getUserId()));
        }
        return deviceInfoMapper.selectGis(deviceInfo);
    }

    /**
     * 查询指定读写器的最后一次上传数据
     *
     * @param deviceId 读写器物理ID
     * @return 上传数据
     */
    @Override
    public DeviceDataVo getLastData(String deviceId)
    {
        DeviceDataVo dataVo = new DeviceDataVo();
        // 先查询物模型
        String modelStr = deviceInfoMapper.selectModel(deviceId);
        ThingModelVo.Model model = JsonUtils.parseObject(modelStr, ThingModelVo.Model.class);
        Map<String, DevicePropertyCache> properties = deviceInfoData.getProperties(deviceId);
        Map<String, Object> dataMap = new HashMap<>();
        model.getProperties().forEach(item ->
        {
            if (properties.containsKey(item.getIdentifier())) {
                String label = item.getName();
                DevicePropertyCache devicePropertyCache = properties.get(item.getIdentifier());
                dataVo.setTime(devicePropertyCache.getOccurred());
                Object value = new Object();
                if (item.getDataType().getType().equals("enum")) {
                    // 属性为枚举类型则取出具体值
                    Map<String, Object> specs = (Map<String, Object>) item.getDataType().getSpecs();
                    value = specs.get(devicePropertyCache.getValue() + "");
                } else {
                    value = devicePropertyCache.getValue();
                }
                dataMap.put(label, value + (item.getUnit() == null ? "" : item.getUnit()));
            }
        });
        if (dataMap.size() > 0) {
            dataVo.setData(dataMap);
        }
        return dataVo;
    }

    /**
     * 导出xlx
     */
    @Override
    public void export(HttpServletResponse response, ExportParam exportParam)
    {
        if (SecurityUtils.hasRole("usergroup")) {
            exportParam.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
        } else if (SecurityUtils.hasRole("emp")) {
            exportParam.setUserId(String.valueOf(SecurityUtils.getUserId()));
        }
        List<DeviceTableEntity> list = deviceInfoMapper.selectExportData(exportParam);
        list.forEach(item -> item.setWorkName(item.getMode() == 0 ? "被动模式" : "定时模式"));
        ExcelUtil<DeviceTableEntity> util = new ExcelUtil<DeviceTableEntity>(DeviceTableEntity.class);
        util.exportExcel(response, list, "设备数据");
    }

    /**
     * 批量删除读写器
     *
     * @param deviceIds 读写器物理ID
     */
    @Override
    @Transactional
    public void batchDel(List<String> deviceIds)
    {
        deviceIds.forEach(this::deleteById);
    }

    /**
     * 查询指定产品下的读写器设备
     *
     * @param productKey 产品key
     */
    @Override
    public boolean hasDeviceOfProductKey(String productKey)
    {
        return deviceInfoMapper.selectProductChild(productKey) > 0;
    }

    /**
     * 查询读写器下的标签
     *
     * @param deviceId 读写器物理ID
     */
    @Override
    public List<TagVo> getChild(String deviceId)
    {

        return tagInfoService.getByParentId(deviceId);
    }

    /**
     * 加载读写器缓存数据
     */
    @Override
    public void loadingDeviceCache()
    {
        List<DeviceTagEntity> cacheData = deviceInfoMapper.getCacheData();
        Map<String, List<DeviceTagEntity>> deviceMap = cacheData.stream().collect(Collectors.groupingBy(DeviceTagEntity::getDeviceSbId));

        Map<String, DeviceTagCache> deviceTagCacheMap = new HashMap<>();
        deviceMap.entrySet().stream().forEach(item ->
        {
            DeviceTagCache deviceTagCache = new DeviceTagCache();
            String key = item.getKey();
            List<DeviceTagEntity> value = item.getValue();
            if (CollectionUtil.isNotEmpty(value)) {
                DeviceTagEntity deviceTagEntity = value.get(0);
                HashMap<String, TagCache> tags = new HashMap<>();
                deviceTagCache.setDeviceId(deviceTagEntity.getDeviceId());
                deviceTagCache.setDeviceName(deviceTagEntity.getDeviceName());
                deviceTagCache.setDeviceSbId(deviceTagEntity.getDeviceSbId());
                deviceTagCache.setProductKey(deviceTagEntity.getPId1());
                deviceTagCache.setDeptId(deviceTagEntity.getDeptId());
                deviceTagCache.setUserId(deviceTagCache.getUserId());
                value.forEach(tag ->
                {
                    if (tag.getTagSbId() != null)
                        tags.put(tag.getTagSbId(), new TagCache(tag.getTagId(), tag.getTagSbId(), tag.getTagName(), tag.getPId2(), tag.getMax(), tag.getMin(), tag.getType(), tag.getState()));
                });
                deviceTagCache.setTags(tags);
                deviceTagCacheMap.put(key, deviceTagCache);
            }
        });

        for (Map.Entry<String, DeviceTagCache> entry : deviceTagCacheMap.entrySet()) {
            setDeviceTagCache(entry.getKey(), entry.getValue());
        }
    }


    @Override
    public Map<String, Boolean> getOnlie(List<String> deviceIds)
    {
        deviceIds.forEach(deviceId ->
        {
            HeartCacheUtils.isOnline(deviceId);
        });
        return null;
    }


    @Override
    public List<String> selectTagIds(String deviceId)
    {
        return deviceInfoMapper.selectTagIds(deviceId);
    }

    @Override
    public List<TagInfo> selectTags(String deviceId)
    {
        return deviceInfoMapper.selectTags(deviceId);
    }

    @Override
    public String resetDevice(String sbId)
    {
        String handlerId = DataReader.readHandleMap.get(sbId);
        TcpAnsEntity tcpAns = new TcpAnsEntity();
        tcpAns.setReaderId(sbId);
        tcpAns.setOptType(DEVICE_RESET);
        tcpAns.setParam("");
        tcpAns.setCrc("ff");
        TcpServerVerticle tcpServerVerticle = SpringUtils.getBean(TcpServerVerticle.class);
        tcpServerVerticle.sendMsg(handlerId, tcpAns.toString());
        return "success";
    }
}
